package com.segersten.othello.service.akka.lobby;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import scala.Option;

import akka.actor.ActorRef;
import akka.actor.UntypedActor;

import com.segersten.othello.service.akka.AkkaUtil;
import com.segersten.othello.service.akka.Auth;
import com.segersten.othello.service.akka.GameObjectPool;
import com.segersten.othello.service.akka.GameObjectReference;
import com.segersten.othello.service.akka.Player;
import com.segersten.othello.service.akka.game.BoardActorFactory;
import com.segersten.othello.service.akka.game.SessionLogginAction;

public class LobbyServiceActor extends UntypedActor {

	public static final String SERVICE_NAME = "lobby";
	private Map<DomainPlayer, Ticket> registeredForMatching;
	private PlayerScores playerScores;
	private BoardActorFactory boardActorFactory;
	private LoginSessionRegistry loginSessionRegistry;
	private GameObjectPool gameObjectPool;
	
	public LobbyServiceActor(GameObjectPool gameObjectPool, BoardActorFactory factory) {
	    loginSessionRegistry = new LoginSessionRegistry();
		this.playerScores = new PlayerScores();
		registeredForMatching = new HashMap<DomainPlayer, Ticket>();
		this.boardActorFactory = factory;
		this.gameObjectPool = gameObjectPool;
	}
	
	@Override
	public void preStart() {
		super.preStart();
	}

	@Override
	public void preRestart(Throwable reason, Option<Object> message) {
		super.preRestart(reason, message);
	}

	@Override
	public void postRestart(Throwable reason) {
		super.postRestart(reason);
	}

	@Override
	public void postStop() {
		super.postStop();
	}
	
	@Override
	public void onReceive(Object message) throws Exception {
		System.out.println("LobbyServiceActor.Received " + message);
		if (message instanceof LoginAction) {
		    LoginAction action = (LoginAction) message;
            handleLoginAction(action);
        } else if (message instanceof MatchOpponentsAction) {
			handleMatchOpponents();
		} else if (message instanceof RegisterAction) {
			RegisterAction action = (RegisterAction) message;
			handleRegister(action);
		} else if (message instanceof GameFinishedAction) {
			GameFinishedAction action = (GameFinishedAction) message;
			handleGameFinished(action);
		} else if (message instanceof StateAction) {
			StateAction action = (StateAction) message;
			tellState(action);
		} else if (message instanceof PlayerScoreAction) {
			PlayerScoreAction action = (PlayerScoreAction) message;
			tellScore(action);
		} else if (message instanceof CancelTicketAction) {
			CancelTicketAction action = (CancelTicketAction) message;
			cancelTickets(action);
		} else if (message instanceof String) {
	            String action = (String) message;
                handleString(action);
		} else {
			throw new IllegalArgumentException("Unknown message: " + message);
		}
	}

	private void handleString(String action)
    {
       if ("sessions".equals(action)) {
           AkkaUtil.channelTell(this, loginSessionRegistry.getAllSessions());
       }
    }

    private void handleLoginAction(LoginAction action)
    {
	    Player player = action.getPlayer();
        String nick = player.getNickname();
	    String domain = player.getAuth().getDomain();
        Auth replacedSession = loginSessionRegistry.addSession(domain, nick, player.getAuth());
        System.out.println("Replaced login " + replacedSession);
        if (replacedSession != null) {
            List<Game> games = playerScores.getOngoingGames(replacedSession);
            for (Game game : games) {
                GameObjectReference ref = gameObjectPool.actorFor(game.getBoardReference().getReference());
                gameObjectPool.tell(ref, new SessionLogginAction(replacedSession, player));
            }
            playerScores.replace(replacedSession, player.getAuth());
        } else {
            playerScores.ensurePlayerScore(action.getPlayer());
        }
	    
    }

    private void cancelTickets(CancelTicketAction action) {
        if (!loginSessionRegistry.isAuthorized(action.getAuth())) {
            AkkaUtil.channelTell(this, LobbyResponseStatus.NonAuth);
            return;
        }
		DomainPlayer dp = loginSessionRegistry.getDomainPlayer(action.getAuth());
		registeredForMatching.remove(dp);
		AkkaUtil.channelTell(this, LobbyResponseStatus.OK);
	}

	private void tellScore(PlayerScoreAction action) {
		String nickname = action.getPlayerId();
		AkkaUtil.channelTell(this, playerScores.getPoints(nickname));
	}

	private void handleGameFinished(GameFinishedAction action) {
		playerScores.gameFinished(action.getBoardReference(), action.getScores());
	}

	private void handleMatchOpponents() {
	    
		while (registeredForMatching.size() >= 2) {
			//System.out.println("Match " + registeredForMatching);
			Iterator<Entry<DomainPlayer, Ticket>> iter = registeredForMatching.entrySet().iterator();
			Entry<DomainPlayer, Ticket> e1 = iter.next();
			Entry<DomainPlayer, Ticket> e2 = iter.next();
			registeredForMatching.remove(e1.getKey());
			registeredForMatching.remove(e2.getKey());

			// Create an actorReference
			DomainPlayer domainPlayer1 = e1.getValue().getDomainPlayer();
			DomainPlayer domainPlayer2 = e2.getValue().getDomainPlayer();
			Player player1 = loginSessionRegistry.getPlayer(domainPlayer1);
			Player player2 = loginSessionRegistry.getPlayer(domainPlayer2);
            String actorRef = boardActorFactory.createBoard(player1, player2);
			//String actorRef = AkkaUtil.getReference(actor);

			BoardReference boardReference = new BoardReference(actorRef);
			playerScores.addGame(boardReference, player1, player2);
		}
		AkkaUtil.channelTell(this, LobbyResponseStatus.OK);
	}

	private void handleRegister(RegisterAction action) {
	    Player player = action.getPlayer();
	    
	    if (false == loginSessionRegistry.isAuthorized(player.getAuth())) {
            AkkaUtil.channelTell(this, LobbyResponseStatus.NonAuth);
            return;
        }
	    
        DomainPlayer domainPlayer = new DomainPlayer(player.getAuth().getDomain(), player.getNickname());
        registeredForMatching.put(domainPlayer, new Ticket(domainPlayer));
		AkkaUtil.channelTell(this, LobbyResponseStatus.OK);
	}
	
	private void tellState(StateAction action) {
	    Auth auth = action.getAuth();
		if (this.loginSessionRegistry.isAuthorized(auth)==false) {
		    AkkaUtil.channelTell(this, new StateResponse(LobbyResponseStatus.NonAuth));
		}
		DomainPlayer dp = loginSessionRegistry.getDomainPlayer(auth);
		LobbyState stateResponse = new LobbyState(this.registeredForMatching.get(dp), this.playerScores.getOngoingGames(auth));
		AkkaUtil.channelTell(this, new StateResponse(stateResponse));
	}

}
